/*	--- 适配器 --- 


# 将【一个类的接口】转换成【客户】希望的【另外一个接口】，使得原本由于【接口不兼容而不能一起工作的那些类】可以【一起工作】。

========================================================


【适配器模式】是【Adapter】，也称【Wrapper】，是指: 如果【一个接口】需要【B接口】，但是【待传入的对象】却是【A接口】，怎么办？
	B Function(Interface A)


我们举个例子: 如果去美国，我们随身带的电器是无法直接使用的，因为美国的插座标准和中国不同，所以，我们需要一个【适配器】： 


在程序设计中，【适配器】也是类似的。

我们已经有一个【Task类】，实现了【Callable接口】：*/
public class Task implements Callable<Long> {

	private long num;

	public Task(long num) {
		this.num = num;
	}

	public Long call() throws Exception {
		long r = 0;

		for (long n=1; n<=this.num; n++) {
			r = r + n; // n!
		}

		System.out.println("Result: " + r);

		return r;
	}
}


/*
现在，我们想通过【一个线程】去执行它： */
Callable<Long> callable = new Task(1234500000L);
Thread thread = new Thread(callable); // compile error!
thread.start();


/*
发现【编译】不过！

因为【Thread】接收【Runnable接口】，但不接收【Callable接口】，肿么办？

一个办法是【改写Task类】，把【实现的Callable】改为【Runnable】，但这样做不好，因为【Task】很可能在其他地方【作为Callable被引用】，改写【Task的接口】，会导致【其他】正常工作的代码【无法编译】。

--------------------

另一个办法【不用改写Task类】，而是【用一个Adapter】，把这个【Callable接口】“变成”【Runnable接口】，这样，就可以正常编译： */
Callable<Long> callable = new Task(1234500000L);
Thread thread = new Thread(new RunnableAdapter(callable));
	// implements Callable<Long> -> implements Runable<Long>
thread.start();


/*
这个【RunnableAdapter类】就是【Adapter】，它接收一个 Callable，输出一个 Runnable。

怎么实现这个【RunnableAdapte】r呢？

我们先看完整的代码： */
public class RunnableAdpater implements Runnable {
	// 引用待转换接口: 
	private Callable<?> callable;

	public RunnableAdapter(Callable<?> callable) {
		this.callable = callable;
	}

	// 实现指定接口
	public void run() { // Callable.call() -> Runnable.run()
		// 将【指定接口调用】委托给【转换接口调用】
		try {
			callable.call();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}


/*
编写一个Adapter的步骤如下：

	1. 实现【目标接口】，这里是【Runnable】；
	2. 内部持有一个【待转换接口的引用】，这里是通过【字段】持有【Callable接口】；
	3. 在【目标接口的实现方法】内部，调用【待转换接口的方法】。


这样一来，【Thread】就可以接收这个【RunnableAdapter】，因为它实现了【Runnable接口】。

【Thread】作为【调用方】，它会调用【RunnableAdapter的run()方法】，在这个【run()方法内部】，又调用了【Callable的call()方法】，相当于【Thread】通过【一层转换】，间接调用了【Callable的call()方法】。


【适配器模式】在【Java标准库】中有【广泛应用】。

比如我们持有【数据类型】是String[]，但是需要【List接口】时，可以用一个【Adapter】：*/
String[] exist = new String[] { "Good", "morning", "Bob", "and", "Alice" };
Set<String> set = new HashSet<>(Arrays.asList(exist));
	// Arrays.asList(T[])的.asList()就是Adapter


/*
注意到 List<T> Arrays.asList(T[]) 就相当于一个【转换器】，它可以把【数组】转换为【List】。



我们再看一个例子：  

Q: 假设我们持有一个【InputStream】，希望调用【readText(Reader)方法】，但它的【参数类型是Reader】而不是InputStream，怎么办？

A: 当然是使用适配器，把【InputStream】“变成”【Reader】： */
InputStream input = Files.newInputStream(Paths.get("/path/to/file"));
	// Files.newInputStream(Paths.get("...")) 获取InputStream
Reader reader = new InputStreamReader(input, "UTF-8");
readText(reader);
	// new InputStreamReader()就是Adapter


/*
【InputStreamReader】就是【Java标准库】提供的【Adapter】，
【InputStreamReader】负责把一个【InputStream】适配为【Reader】。

类似的还有【OutputStreamWriter】。

-------------

Q: 如果我们把【readText(Reader)方法参数】从【Reader】改为【FileReader】，会有什么问题？

A: 这个时候，因为我们需要一个【FileReader类型】，就必须把【InputStream】适配为【FileReader】： */
FileReader reader = new InputStreamReader(input, "UTF-8"); 
	// compile error!


/*
直接使用【InputStreamReader】这个Adapter是不行的，因为它只能转换出【Reader接口】。

事实上，要把【InputStream】转换为【FileReader】也不是不可能，但需要花费十倍以上的功夫。

这时，【面向抽象编程】这一【原则】就体现出了威力：持有【高层接口】不但代码更灵活，而且把【各种接口】组合起来也更容易。

一旦持有【某个具体的子类类型】，要想做一些改动就非常困难。



=========================================================


#	练习

使用Adapter模式将Callable接口适配为Runnable。 */




/*==================================================


#	----- 适配器 の 小结 ----- 

1. 【Adapter模式】可以将一个【A接口】转换为【B接口】，使得【新的对象】符合【B接口规范】。

2. 在【Adapter内部】将【B接口的调用】“转换”为【对A接口的调用】。

3. 只有【A、B接口】均为【抽象接口】时，才能非常简单地【实现Adapter模式】。

4. 【编写Adapter】实际上就是【编写一个实现了对外B接口, 并且内部持有A接口的类】： */
public B_Adapter implements B {
	private A a;

	public B_Adapter(A a) {
		this.a = a;
	}

	public void b() {
		a.a();
	}
}




















